package util;

import org.apache.commons.dbcp2.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * dao层工具类
 */
public class BaseDao {
    static final DataSource dataSource;

    /**
     * 获取数据源
     */
    static {
        InputStream inputStream = BaseDao.class.getResourceAsStream("/jdbc.properties");
        Properties properties = new Properties();
        try {
            properties.load(inputStream);
            dataSource = BasicDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取数据库连接
     *
     * @return 数据库连接对象
     */
    public static Connection getConnection() {
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * 释放相关连接
     *
     * @param resultSet  结果集对象
     * @param statement  载体对象
     * @param connection 数据库连接对象
     */
    public static void release(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 增删改通用方法
     *
     * @param sql     sql语句
     * @param objects 值
     * @return 影响行数
     */
    public static int cud(String sql, Object... objects) {
        Connection connection = getConnection();
        PreparedStatement preparedStatement = null;
        int result = 0;
        try {
            preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < objects.length; i++) {
                preparedStatement.setObject(i + 1, objects[i]);
            }
            result = preparedStatement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            release(null, preparedStatement, connection);
        }
        return result;
    }

    public static <T> T domain(IDBOperation<T> interfaces) {
        return domain(interfaces, "数据库异常");
    }

    /**
     * 执行封装的数据库操作，捕获抛出的异常。<br>
     * 如果有异常发生并被捕获，此次事务中所有数据库操作将被回滚。<br>
     * 此方法会自动从连接池获取数据库连接对象，并在方法结束时释放该连接。
     *
     * @param operation 封装的数据库操作
     * @param <T>       数据库操作返回对象泛型
     * @param errmsg    发生异常时日志记录信息
     * @return 数据库操作返回的对象
     * @see IDBOperation#action(Connection)
     */
    public static <T> T domain(IDBOperation<T> operation, String errmsg) {
        T Object = null;
        Connection con = new BaseDao().getConnection();
        try {
            con.setAutoCommit(false);
            Object = operation.action(con);
            con.commit();
        } catch (Exception e) {
            try {
                con.rollback();
                e.printStackTrace();
            } catch (SQLException e1) {
            }
        } finally {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return Object;
    }

    /**
     * 数据库操作封装的函数接口
     *
     * @param <T> 数据库操作返回对象类型，如没有返回值，必须返回<code>null</code>
     */
    @FunctionalInterface
    public interface IDBOperation<T> {
        /**
         * 封装的数据库操作
         * 注意，不建议在此操作中调用{@link Connection#commit()} 方法
         * 所有的数据库操作应在结束后由封装方法自动commit
         *
         * @param con 数据库连接对象
         * @return 返回泛型指定的对象，或者没有返回值时返回<code>null</code>
         * @throws SQLException 数据库操作发生异常时抛出
         */
        T action(Connection con) throws SQLException;
    }
}
